const childProcess = require('child_process');
const EventEmitter = require('events');
const stream = require('stream');
const parseGdbMiOut = require('gdb-mi-parser');
const async = require('async');
const { Utils } = require('../utils');
let myfunction = new Utils;

class Gdb extends EventEmitter {
    constructor() {
        super();
        this.threads = new Array;
        //this.gdb = childProcess.spawn('gdb', ['-q', '--interpreter', 'mi']);
        // this.gdb = childProcess.spawn('gdb');
        this.outputBuffer = '';
        // this.gdb = childProcess.exec('source /data/system/liucj/teco/test/gdbenv.sh && export SDAA_ENABLE_COREDUMP_ON_EXCEPTION=1 && tecogdb', { shell: '/bin/bash' });
        this.gdb = childProcess.exec('source /opt/tecoai/setvars.sh && export SDAA_ENABLE_COREDUMP_ON_EXCEPTION=1 && tecogdb', { shell: '/bin/bash' });
        this.gdb.stdout.setEncoding('utf8');
        this.gdb.stderr.setEncoding('utf8');
        this.tecoInfo = '';
        this.stdOutErrPassThrough_1 = new stream.PassThrough();
        this.stdOutErrPassThrough_1.setEncoding('utf8');

        this.stdOutErrPassThrough_2 = new stream.PassThrough();
        this.stdOutErrPassThrough_2.setEncoding('utf8');

        this.stdOutErrPassThrough_3 = new stream.PassThrough();
        this.stdOutErrPassThrough_3.setEncoding('utf8');


        // this.gdb.stdout.pipe(this.stdOutErrPassThrough_1);
        // this.gdb.stderr.pipe(this.stdOutErrPassThrough_1);

        this.gdb.stdout.pipe(this.stdOutErrPassThrough_2);
        this.gdb.stderr.pipe(this.stdOutErrPassThrough_2);

        this.gdb.stdout.pipe(this.stdOutErrPassThrough_3);
        this.gdb.stderr.pipe(this.stdOutErrPassThrough_3);

        this.stdOutErrPassThrough_2.on('data', (data) => {
            // console.log(data);
            // let parsed = data;
            // this.emit('dataStream', data.split(/\^|\*|\=|\~"|\&"|\(gdb\)/g)[0]);
            // let parsed = parseGdbMiOut(data);
            // parsed.outOfBandRecords.map((x) => {
            //     if (x.recordType === 'stream') {
            //         if (x.outputType === 'console') {
            //             return x.result;
            //         }
            //     }
            // }).filter((x) => x).forEach((str) => {
            //     this.emit('dataStream', str);
            //     if (str.includes('No more reverse-execution history')) {
            //         this.emit('end');
            //     }
            //     if (str.includes('The next instruction is syscall exit')) {
            //         this.emit('end');
            //     }
            // });

            // parsed.outOfBandRecords.forEach((record) => {
            //     switch (record.class) {
            //         case 'thread-created' :
            //              this.threads.push(record.result.id);
            //              break;
            //         case 'breakpoint-created':
            //         case 'breakpoint-modified':
            //             this.emit('breakpointModified', record);
            //             break;
            //         case 'stopped':
            //             this.emit('stopped', record);
            //             break;
            //         // and more to come
            //     }
            // });
        });

        this.stdOutErrPassThrough_3.on('data', (data) =>{
            let text = data.toString();
            this.outputBuffer += text;
        });

        this.myqueue = async.queue((command, callback) => {
            this.gdb.stdin.write(command);
            let resp = this.getGdbResponse(command);
            callback(resp);
        }, 1);

        this.disableRaw = () => (this.rawDisabled = true);
        this.rawDisabled = false;
        this.result = [];
        this.registers = [];
    }

    initSendCommand() {
        this.gdb.stdout.pipe(this.stdOutErrPassThrough_1);
        this.gdb.stderr.pipe(this.stdOutErrPassThrough_1);
    }


    async send(command) {
        let ans = await new Promise((resolve) => {
            this.myqueue.push(command, resolve);
        });
        return ans['data'];
    }

    async offlinesend(command) {
        this.gdb.stdin.write(command);
        await new Promise(resolve => setTimeout(resolve, 500));
        let output = this.outputBuffer;
        this.outputBuffer = '';
        return output;
    }

    async getGdbResponse(cmd) {
        let res = await new Promise((res) => {
            let callback = (data) => {
                // let parsed = parseGdbMiOut(data);
                if (data.indexOf('(teco-gdb)') != -1) {
                    let parsed = this.tecoInfo + data;
                    // console.log("sunstart=================");
                    // console.log(cmd);
                    // console.log(parsed);
                    // console.log("sunend=================");
                    this.tecoInfo = '';
                    this.stdOutErrPassThrough_1.removeListener('data', callback);
                    // console.log("sunstart  => ", cmd, parsed, " <== sunend")
                    res({cmd : cmd, data : parsed});
                } else {
                    this.tecoInfo += data;
                }
            };
            this.stdOutErrPassThrough_1.on('data', callback);
        });
        return res;
    }

    sendRaw(command) {
        this.gdb.stdin.write(command);
    }

    async initRegisters() {
        // let resultRecord = await this.send('-data-list-register-names\n');
        // if (resultRecord.resultRecord && resultRecord.resultRecord.class === 'done') {
        //     this.registers = resultRecord.resultRecord.result['register-names'];
        // }
    }

    async getRegisterValues(registers = []) {
        if (registers.length === 0) {
            registers = this.registers.filter((r) => r);
        }
        let numbers = [];
        numbers = registers.map((reg) => {
            return this.registers.indexOf(reg);
        });
        let result = {};
        let values;
        // let values = await this.send(`-data-list-register-values x ${numbers.join(' ')}\n`);
        if (values && values.resultRecord && values.resultRecord.class === 'done') {
            values.resultRecord.result['register-values'].forEach((data) => {
                result[this.registers[data.number]] = data.value;
            });
        }

        return result;
    }

    async getVariablesValue(name){
        let values;
        // let values = await this.send(`-var-evaluate-expression ${name}\n`);
        return values;
    }

    isRegister(reg) {
        return this.registers.find((v) => v === reg);
    }
}

module.exports = {Gdb};
